package de.justphil.websocketclient;

import java.io.IOException;
import java.io.InputStream;


public class WebSocketReceiver extends Thread {
	
	private InputStream input = null;
	private WebSocketConnection websocket = null;
	private WebSocketListener webSocketListener = null;

	private volatile boolean stop = false;

	
	public WebSocketReceiver(InputStream input, WebSocketConnection websocket) {
		this.input = input;
		this.websocket = websocket;
		this.webSocketListener = websocket.getWebSocketListener();
	}

	
	
	/*
	 * 
	 *  0                   1                   2                   3
     *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     * +-+-+-+-+-------+-+-------------+-------------------------------+
     * |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     * |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     * |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     * | |1|2|3|       |K|             |                               |
     * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     * |     Extended payload length continued, if payload len == 127  |
     * + - - - - - - - - - - - - - - - +-------------------------------+
     * |                               |Masking-key, if MASK set to 1  |
     * +-------------------------------+-------------------------------+
     * | Masking-key (continued)       |          Payload Data         |
     * +-------------------------------- - - - - - - - - - - - - - - - +
     * :                     Payload Data continued ...                :
     * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     * |                     Payload Data continued ...                |
     * +---------------------------------------------------------------+
	 * 
	 * 
	 */
	
	

	public void run() {
		int b = 0;
		while (!stop) {
			try {
				b = input.read();
				
				if (b >= 128) {
					// FIN bit set -> unfragmented message
					
					if (b == 0x81) {
						// op code == %x1 -> text frame
						handleTextFrame();
					}
					else if (b == 0x82) {
						// op code == %x2 -> binary frame
						//throw new RuntimeException("Receiving of binary frames has NOT been implemented yet!");
						System.out.println("Receiving of binary frames has NOT been implemented yet!");
					}
					else if (b == 0x88) {
						// op code == %x8 -> connection close
						handleCloseFrame();
					}
				}
				else if (b == -1) {
					handleCloseFrame();
				}
				else {
					// FIN bit NOT set -> fragmented message
					//throw new RuntimeException("Receiving of fragmented messages has NOT been implemented yet!");
					System.out.println("Receiving of fragmented messages has NOT been implemented yet!");
				}
			}
			catch (IOException ioe) {
				handleError();
			}
		}
		
		System.out.println("WebSocketReceiver killed!");
	}
	
	
	public void stopIt() {
		stop = true;
	}
	
	
	public boolean isRunning() {
		return !stop;
	}
	
	
	private void handleError() {
		stopIt();
		websocket.handleReceiverError();
	}
	
	private void handleTextFrame() {
		long payloadSize = 0;
		
		try {
			int m = input.read();
			
			if (m >= 128) {
				// payload is masked
			}
			else {
				// payload is NOT masked
				
				if (m <= 125) {
					// 7 bit payload length
					payloadSize = m;
				}
				else if (m == 126) {
					// following 2 bytes (16 bit) determine the payload length
					int p1 = input.read();
					int p2 = input.read();
					
					payloadSize = (p1 << 8) | p2;
				}
				else if (m == 127) {
					// following 8 bytes (64 bit) determine the payload length
					int p1 = input.read();
					int p2 = input.read();
					int p3 = input.read();
					int p4 = input.read();
					int p5 = input.read();
					int p6 = input.read();
					int p7 = input.read();
					int p8 = input.read();
					
					payloadSize = (p1 << 8) | p2;
					payloadSize = (payloadSize << 8) | p3;
					payloadSize = (payloadSize << 8) | p4;
					payloadSize = (payloadSize << 8) | p5;
					payloadSize = (payloadSize << 8) | p6;
					payloadSize = (payloadSize << 8) | p7;
					payloadSize = (payloadSize << 8) | p8;
				}
				
				
				if (payloadSize > Integer.MAX_VALUE) {
					//throw new RuntimeException("Payload length > " + Integer.MAX_VALUE + " bytes is not supported yet.");
					System.out.println("Payload length > " + Integer.MAX_VALUE + " bytes is not supported yet.");
				}
				else {
					byte[] payload = new byte[ (int) payloadSize];
					input.read(payload, 0, payload.length);
					
					if (webSocketListener == null) {
						System.out.println("webSocketListener is null");
					}
					else {
						webSocketListener.onMessage(new WebSocketMessage(payload));
					}
				}
			}
		}
		catch (IOException ioe) {
			handleError();
		}
	}
	
	private void handleCloseFrame() {
		websocket.closeSilently();
	}
}
